﻿using System;
using System.IO.Ports;
using System.Threading;

namespace TestFireBroadcastingHostPhone.FrockBorad
{
    public class FrockBorad
    {
        private protected SerialPort? FrockSerialport;
        /// <summary>
        /// 工装板的波特率
        /// </summary>
        public const int frockborad_baudrate = 9600;
        /// <summary>
        /// 控制工装IO口输出的字节
        /// </summary>
        private byte IO_Put_Contorl_Data = 0x00;
        /// <summary>
        /// 工装板串口协议定义
        /// </summary>
        internal struct IngenuBorderCmd_Struct
        {
            public const byte FrameHeader_Detector = 0x68;//帧头
            public const byte SettingIOPutCommand_Detector = 0x21;//设置盈帜工装板IO口输出命令
            public const byte SettingIO_PutWayCommand = 0x20;//设置盈帜工装板IO输出方式命令
            public const byte ReadIO_ADCCommand = 0x10;//读取工装板IO口ADC命令
            public const byte Setting_LED_Status = 0x23; //设置LED状态命令
            public const byte ExchangePackage_Cmd = 0x30;//转包命令
            public const byte ReadAddress_Failure = 0x16;//帧尾
        }


        #region -- 公共函数
        /// <summary>
        /// 串口数据发送
        /// </summary>
        /// <param name="send_data"></param>
        /// <param name="errinfo"></param>
        public void SendData(byte[] send_data, out string errinfo)
        {
            errinfo = string.Empty;
            if (FrockSerialport is null)
            {
                errinfo = "未配置串口!";
                return;
            }
            try
            {
                if (!FrockSerialport.IsOpen)
                {
                    FrockSerialport.Open();
                }
                if (!string.IsNullOrEmpty(FrockSerialport.PortName) && FrockSerialport.IsOpen)
                {
                    FrockSerialport.ReadExisting();                          //发送前清缓冲区
                    FrockSerialport.Write(send_data, 0, send_data.Length);
                }
                else
                {
                    errinfo = "串口未打开，无法发送数据";
                }
            }
            catch (Exception ex)
            {
                errinfo = "发送数据出现错误" + ex;
            }
        }
        /// <summary>
        /// 等待串口数据接收
        /// </summary>
        /// <param name="wait_timer">串口超时等待时间</param>
        /// <param name="setting_lenth">串口接收数据长度</param>
        public byte[] Receive_Wait(int wait_timer, int setting_lenth, out string err_info)
        {
            int data_lenth = 0;
            byte[] receive_data = new byte[setting_lenth];
            err_info = string.Empty;
            if (FrockSerialport is null)
            {
                err_info = "未配置串口!";
                return receive_data;
            }
            try
            {
                for (int b = 0; b < wait_timer; b++)   //最长等待500ms
                {
                    Thread.Sleep(10);                  //等待10ms
                    data_lenth = FrockSerialport.BytesToRead;   //获取接收数据长度
                    if (data_lenth >= setting_lenth) break;       //避开接收异常，如上电断电引起的0X00
                }
                if (data_lenth == setting_lenth)
                {
                    Array.Resize(ref receive_data, data_lenth);               //重定义数组长度
                    FrockSerialport.Read(receive_data, 0, data_lenth);      //读取数据
                }
                else
                {
                    err_info = "规定时间内未接收到指定数据";
                }
            }
            catch (Exception ex)
            {
                err_info = $"Error reading data_red: {ex.Message}";
            }
            return receive_data;
        }
        /// <summary>
        /// 工装板校验和计算
        /// </summary>
        /// <param name="input_data">输入数据</param>
        /// <returns>返回结果</returns>
        private static byte Test_Board_Check_Sum_Cal(byte[] input_data)
        {
            byte result = 0;

            int sum = 0;

            for (int a = 5; a < input_data.Length - 2; a++)
            {
                sum += input_data[a];
            }

            result = (byte)sum;

            return result;
        }

        #endregion //

        #region -- 数据填充
        /// <summary>
        /// 控制IO输出命令的数据填充
        /// </summary>
        /// <param name="border_address">工装地址</param>
        /// <param name="data5">控制字节</param>
        /// <returns>发送控制IO输出数据</returns>
        private byte[] Control_IO(byte border_address, byte data5)
        {
            byte[] send_cmd = new byte[8];
            send_cmd[0] = IngenuBorderCmd_Struct.FrameHeader_Detector;
            send_cmd[1] = border_address;
            send_cmd[2] = IngenuBorderCmd_Struct.SettingIOPutCommand_Detector;
            send_cmd[3] = (byte)(~send_cmd[2]);
            send_cmd[4] = 1;
            send_cmd[5] = data5;
            send_cmd[7] = IngenuBorderCmd_Struct.ReadAddress_Failure;
            send_cmd[6] = Test_Board_Check_Sum_Cal(send_cmd);
            return send_cmd;
        }
        /// <summary>
        /// 控制IO口输出方式命令的数据填充
        /// </summary>
        /// <param name="border_address">工装板地址</param>
        /// <param name="data5">工装板的IO输出方式</param>
        /// <returns></returns>
        private byte[] Cmd_IOPutWay(byte border_address, byte data5)
        {
            byte[] send_cmd = new byte[8];
            send_cmd[0] = IngenuBorderCmd_Struct.FrameHeader_Detector;
            send_cmd[1] = border_address;
            send_cmd[2] = IngenuBorderCmd_Struct.SettingIO_PutWayCommand;
            send_cmd[3] = (byte)(~send_cmd[2]);
            send_cmd[4] = 1;
            send_cmd[5] = data5;
            send_cmd[7] = IngenuBorderCmd_Struct.ReadAddress_Failure;
            send_cmd[6] = Test_Board_Check_Sum_Cal(send_cmd);
            return send_cmd;
        }
        /// <summary>
        /// 控制LED状态的数据填充
        /// </summary>
        /// <param name="border_address">工装板地址</param>
        /// <param name="data5">控制字节</param>
        /// <returns></returns>
        private byte[] Cmd_LEDControl(byte border_address, byte data5)
        {
            byte[] send_cmd = new byte[8];
            send_cmd[0] = IngenuBorderCmd_Struct.FrameHeader_Detector;
            send_cmd[1] = border_address;
            send_cmd[2] = IngenuBorderCmd_Struct.Setting_LED_Status;
            send_cmd[3] = (byte)(~send_cmd[2]);
            send_cmd[4] = 1;
            send_cmd[5] = data5;
            send_cmd[7] = IngenuBorderCmd_Struct.ReadAddress_Failure;
            send_cmd[6] = Test_Board_Check_Sum_Cal(send_cmd);
            return send_cmd;
        }
        /// <summary>
        /// 转包数据填充
        /// </summary>
        /// <param name="border_address">工装板的地址</param>
        /// <returns></returns>
        private byte[] ExchangePackage(byte border_address)
        {
            byte[] send_cmd = new byte[10];
            send_cmd[0] = IngenuBorderCmd_Struct.FrameHeader_Detector;
            send_cmd[1] = border_address;
            send_cmd[2] = IngenuBorderCmd_Struct.ExchangePackage_Cmd;
            send_cmd[3] = (byte)(~send_cmd[2]);
            send_cmd[4] = 3;
            send_cmd[5] = 0xAA;
            send_cmd[6] = 0xBB;
            send_cmd[7] = 0xCC;
            send_cmd[9] = IngenuBorderCmd_Struct.ReadAddress_Failure;
            send_cmd[8] = Test_Board_Check_Sum_Cal(send_cmd);
            return send_cmd;
        }
        /// <summary>
        /// 读取工装板的ADC
        /// </summary>
        /// <param name="address">工装板地址</param>
        /// <returns></returns>
        public byte[] Cmd_vReadADC(byte address)
        {
            byte[] send_cmd = new byte[8];
            send_cmd[0] = IngenuBorderCmd_Struct.FrameHeader_Detector;
            send_cmd[1] = address;
            send_cmd[2] = IngenuBorderCmd_Struct.ReadIO_ADCCommand;
            send_cmd[3] = (byte)(~send_cmd[2]);
            send_cmd[4] = 1;
            send_cmd[5] = 0x00;
            send_cmd[7] = IngenuBorderCmd_Struct.ReadAddress_Failure;
            send_cmd[6] = Test_Board_Check_Sum_Cal(send_cmd);
            return send_cmd;
        }
        #endregion //

        /// <summary>
        /// 控制工装板IO口输出
        /// </summary>
        /// <param name="IO">IO位号</param>
        /// <param name="status">IO状态,true 为高，false 为低</param>
        public void Set_IOPut(int IO, bool status,out string errinfo)
        {
            byte[] receive_data = new byte[8];//接收缓存
            errinfo = string.Empty;
            switch (IO)
            {
                case 0: if (status) IO_Put_Contorl_Data |= 0x01; else IO_Put_Contorl_Data &= 0xfe; break;//控制网口
                case 1: if (status) IO_Put_Contorl_Data |= 0x02; else IO_Put_Contorl_Data &= 0xfd; break;//控制24V应急
                case 2: if (status) IO_Put_Contorl_Data |= 0x04; else IO_Put_Contorl_Data &= 0xfb; break;//控制备电
                case 3: if (status) IO_Put_Contorl_Data |= 0x08; else IO_Put_Contorl_Data &= 0xf7; break;//控制主电
                case 4: if (status) IO_Put_Contorl_Data |= 0x10; else IO_Put_Contorl_Data &= 0xef; break;//控制满载
                case 5: if (status) IO_Put_Contorl_Data |= 0x20; else IO_Put_Contorl_Data &= 0xdf; break;//控制最小过流
                case 6: if (status) IO_Put_Contorl_Data |= 0x40; else IO_Put_Contorl_Data &= 0xbf; break;//控制最大过流
                default: errinfo = "无效的IO位号"; return;
            }
            for (int i = 0; i < 3; i++)
            {
                SendData(Control_IO(6, IO_Put_Contorl_Data), out errinfo);
                if (string.IsNullOrEmpty(errinfo))
                {
                    receive_data = Receive_Wait(50, 8, out errinfo);
                    if (string.IsNullOrEmpty(errinfo)) break;
                }
                else
                {
                    errinfo += "串口发送数据错误";
                    break;
                }
            }
        }
        /// <summary>
        /// 设置工装板LED灯状态
        /// </summary>
        /// <param name="lamp">选择灯：0 红灯，1 绿灯</param>
        /// <param name="status">亮灯状态：0：关闭，1：点亮 ，2：闪烁</param>
        /// <param name="errinfo">错误信息</param>
        public void Set_LEDStatus(byte lamp,byte status, out string errinfo)
        {
            byte[] receive_data = new byte[8];//接收缓存
            errinfo = string.Empty;
            byte data_red = 0x00;
            byte data_green = 0x00;
            switch (lamp)
            {
                case 0:
                    switch (status)
                    {
                        case 0: data_red &= 0x0f; data_red |= 0x00; break;//红关
                        case 1: data_red &= 0x0f; data_red |= 0x10; break;//红开
                        case 2: data_red &= 0x0f; data_red |= 0x20; break;//红闪
                        default: return;
                    }
                    for (int i = 0; i < 3; i++)
                    {
                        SendData(Cmd_LEDControl(6, data_red), out errinfo);
                        if (string.IsNullOrEmpty(errinfo))
                        {
                            receive_data = Receive_Wait(50, 8, out errinfo);
                            if (string.IsNullOrEmpty(errinfo)) break;
                        }
                        else
                        {
                            errinfo += "串口数据发送错误";
                            break;
                        }
                    }
                    break;
                case 1:
                    switch (status)
                    {
                        case 0: data_green &= 0xf0; data_green |= 0x00; break;//绿关
                        case 1: data_green &= 0xf0; data_green |= 0x01; break;//绿开
                        case 2: data_green &= 0xf0; data_green |= 0x02; break;//绿闪
                        default: return;
                    }
                    for (int i = 0; i < 3; i++)
                    {
                        SendData(Cmd_LEDControl(6, data_green), out errinfo);
                        if (string.IsNullOrEmpty(errinfo))
                        {
                            receive_data = Receive_Wait(50, 8, out errinfo);
                            if (string.IsNullOrEmpty(errinfo)) break;
                        }
                        else
                        {
                            errinfo += "串口数据发送错误";
                            break;
                        }
                    }
                    break;
                default: return;
            }
        }
        /// <summary>
        /// 读取工装板ADC
        /// </summary>
        /// <returns></returns>
        public int Function_ReadADC(out string err_info)
        {
            int adc_value = 0;
            byte[] Get_ADCBytes = new byte[2];
            byte[] receive = new byte[17];
            err_info = string.Empty;
            SendData(Cmd_vReadADC(6), out err_info);//6号工装板读取ADC
            receive = Receive_Wait(50, receive.Length, out err_info);
            if (!string.IsNullOrEmpty(err_info))
            {
                return adc_value;
            }
            else
            {
                if (receive[2] == IngenuBorderCmd_Struct.ReadIO_ADCCommand)
                {
                    Get_ADCBytes[1] = receive[5];
                    Get_ADCBytes[0] = receive[6];
                    adc_value = BitConverter.ToInt16(Get_ADCBytes, 0);
                }
                else
                {
                    err_info = "数据接收错误！";
                }
            }
            return adc_value;
        }
        /// <summary>
        /// 所有IO口初始化指令：IO0为模拟输入，其他IO为推挽输出
        /// </summary>
        public byte[] IO_PullPut_Init = new byte[8] { 0x68, 0x06, 0x20, 0xdf, 0x01, 0x01, 0x01, 0x16 };
        /// <summary>
        /// IO口初始化
        /// </summary>
        /// <param name="err_info"></param>
        public void All_PutInit(out string err_info)
        {
            err_info = string.Empty;
            byte[] receive_data = new byte[8];//接收缓存
            for (int i = 0; i < 3; i++)
            {
                SendData(IO_PullPut_Init, out err_info);
                if (string.IsNullOrEmpty(err_info))
                {
                    receive_data = Receive_Wait(100, 8, out err_info);
                    if (string.IsNullOrEmpty(err_info)) break;
                }
                else
                {
                    err_info += "发送数据出现错误";
                    break;
                }
            }
        }
        /// <summary>
        /// 将所有IO口设置为低电平
        /// </summary>
        /// <param name="errinfo">错误信息</param>
        public void Close_AllPutIO_Init(out string errinfo)
        {
            byte[] receive_data = new byte[8];//接收缓存
            errinfo = string.Empty;
            IO_Put_Contorl_Data = 0x00;
            for (int i = 0; i < 3; i++)
            {
                SendData(Control_IO(6, IO_Put_Contorl_Data), out errinfo);
                if (string.IsNullOrEmpty(errinfo))
                {
                    receive_data = Receive_Wait(50, 8, out errinfo);
                    if (string.IsNullOrEmpty(errinfo)) break;
                }
                else
                {
                    errinfo += "串口数据发送错误";
                    break;
                }
            }
        }
        
    }
}
